package mediator;

import gui.LoginBtn;
import gui.LoginDialog;
import gui.LogoutBtn;
import gui.Main;
import gui.PopupItemAcceptOffer;
import gui.PopupItemDropAuction;
import gui.PopupItemDropOffer;
import gui.PopupItemLaunchOffer;
import gui.PopupItemMakeOffer;
import gui.PopupItemRefuseOffer;
import gui.ProgressRenderer;
import gui.RefreshBtn;
import interfaces.IGui;
import interfaces.IMediator;
import interfaces.INetwork;
import interfaces.IWebService;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import javax.swing.JDialog;
import javax.swing.table.TableColumn;
import network.Network;
import org.apache.log4j.Logger;
import states.StateManager;
import webservice.MockupWebServiceClient;
import webservice.WebService;

public class Mediator implements IGui, IMediator, INetwork, IWebService {

	public Main mainWindow;
	public LoginDialog loginDialog;
	public LoginBtn loginBtn;
	public LogoutBtn logoutBtn;
	public RefreshBtn refreshBtn;
	
	public ConfigReader cr;
	private MockupWebServiceClient mwsc;
	private WebService ws;
	
	StateManager stateMgr;
	Network network;
	public Simulator simulator;
	
	private PopupItemLaunchOffer popLaunchOffer;
	private PopupItemDropOffer popupItemDropOffer;
	private PopupItemAcceptOffer popupItemAcceptOffer;
	private PopupItemRefuseOffer popupItemRefuseOffer;
	private PopupItemMakeOffer popupItemMakeOffer;
	private PopupItemDropAuction popupItemDropAuction;
	
	public int selectedRow;
	private HashMap<String, Integer> offersLaunched;
	
	public static Logger logger = Logger.getLogger(Mediator.class);
	
	public Mediator () {
		
		stateMgr = new StateManager(this);
		mwsc = new MockupWebServiceClient();
		network = new Network(this);
		ws = new WebService();
		offersLaunched = new HashMap<String, Integer>();
		// Simulate
		simulator = new Simulator(this);
	}
	
	//@Override
	public ArrayList<String> getAvailableSellers(String service) {
		return ws.getAvailableSellers(service);
	}
	
	@Override
	public ArrayList<String> refreshSellers(String service) {
		return ws.refreshSellers(service);
	}

	//@Override
	public ArrayList<String> getAvailableBuyers(String service , String seller) {
		return ws.getAvailableBuyers(service,seller);
	}

	@Override
	public void transfer(int row) {
		network.transfer(row);
	}


	@Override
	public void registerLoginBtn(LoginBtn lb) {
		loginBtn = lb;
	}

	
	@Override
	public void registerDialog(JDialog d) {
		loginDialog = (LoginDialog)d;
	}

	@Override
	public void login() {
		cr = new ConfigReader(loginDialog.getUsernameTxt().getText());
		cr.readConfigFile();
		
		int user_type = ws.login(loginDialog.getUsernameTxt().getText(), loginDialog.getPasswordTxt().getText());
		if ( user_type > 0) {
			
			logger.info("User <" + loginDialog.getUsernameTxt().getText() + "> has logged in.");
			loginDialog.setVisible(false);
			mainWindow.frmAuctionHouse.setVisible(true);
			
			String typeColumn;
			if (user_type == 1) {
				typeColumn = "Sellers";
				stateMgr.setBuyerState();
				mainWindow.lblUserType.setText(cr.username + " : " + cr.type);
			}
			else {
				typeColumn = "Buyers";
				stateMgr.setSellerState();
				mainWindow.lblUserType.setText(cr.username + " : " + cr.type);
				// Add services in DB:
				ws.addServices(loginDialog.getUsernameTxt().getText(), cr.services);
				
			}
			
			mainWindow.dtm.addColumn("Service Name");
			mainWindow.dtm.addColumn(typeColumn);
			mainWindow.dtm.addColumn("Price");
			mainWindow.dtm.addColumn("Status");
			mainWindow.dtm.addColumn("Progress");
			
			TableColumn column = mainWindow.table.getColumnModel().getColumn(4);
			column.setCellRenderer(new ProgressRenderer());
			
			if (cr.type.equals("Buyer")) {
				for (String s : cr.services) {
					
					String[] split = s.split(";");
					String service_name = split[0];
					
					mainWindow.dtm.addRow(new String[] {service_name,"","","Inactive", "-1"});
				}
			} 
			else 
			{
				for (String s : cr.services) {
					String[] split = s.split(";");
					String service_name = split[0];
					
					mainWindow.dtm.addRow(new String[] {service_name,"","","", "-1"});
				}
				
				for (int i = cr.services.size() - 1 ; i >= 0 ; i--){
					String service = cr.services.get(i);
					String[] split = service.split(";");
					String service_name = split[0];
					
					ArrayList<String> al = getAvailableBuyers(service_name , cr.username);
					
					int j = 1;
					for (String s : al) {
						String[] split2 = s.split(";");
						String buyer_name = split2[0];
						String price = split2[1];
						String status = split2[2];
						mainWindow.dtm.insertRow(i + 1, new String[]{"",buyer_name,price,status,"0"});
						j++;
					}
					mainWindow.dtm.setValueAt(al.size(), i, 1);
				}
			}
		}
		else {
			loginDialog.getUsernameTxt().setText("");
			loginDialog.getPasswordTxt().setText("");
			
		}
	}

	@Override
	public void registerMainWindow(Main mw) {
		mainWindow = mw;
	}

	@Override
	public void registerLogoutBtn(LogoutBtn lBtn) {
		logoutBtn = lBtn;
	}

	@Override
	public void registerRefreshBtn(RefreshBtn rBtn) {
		refreshBtn = rBtn;
	}	
	
	@Override
	public void logout() {
		String status = new String();
		String service = new String();
		
		if (cr.type.equals("Buyer")) {
			for (int k = 0; k < mainWindow.dtm.getRowCount(); k++) {
				
				status = (String)mainWindow.dtm.getValueAt(k, 3);
				
				// Get service
				if (status.equals(""))
					service = (String)mainWindow.dtm.getValueAt(k, 0);
				
				switch (status) {
				case "Offer Made":
					mwsc.dropOffer(cr.username, service, (String)mainWindow.dtm.getValueAt(k, 1));
					break;

				case "Offer Accepted":
				case "Transfer Started":
				case "Transfer In Progress":
					// TODO: Network Transfer Failed
					break;
				default:
					break;
				}					
			}
		} 
		else {
			for (int k = 0; k < mainWindow.dtm.getRowCount(); k++) {
				
				status = (String)mainWindow.dtm.getValueAt(k, 3);
				
				// Get service
				if (status.equals(""))
					service = (String)mainWindow.dtm.getValueAt(k, 0);
				
				switch (status) {
				case "Offer Made":
					mainWindow.notificationsTxt.setText("You have opened offers. Please close them before logging out.");
					return;

				case "Offer Accepted":
					mainWindow.notificationsTxt.setText("You have accepted offers. Please wait for them to be transfered before logging out.");
					return;
				case "Transfer Started":
				case "Transfer In Progress":
					mainWindow.notificationsTxt.setText("You have offers in transfer. Please wait for them to finish before logging out.");
					return;
				default:
					break;
				}					
			}			
		}
		
		ws.logout(cr.username);
		System.exit(0);	
	}
	
	
	public void mouseRightClick(int x, int y) {
		stateMgr.mouseRightClick(x, y);
	}

	public void registerPopupItemLaunchOffer(PopupItemLaunchOffer popLaunchOf) {
		popLaunchOffer = popLaunchOf;
		
	}

	public void clickPopupItemLaunchOffer() {
		logger.info("Offer launched");
		
		String currentStatus = (String)mainWindow.dtm.getValueAt(selectedRow, 3);
		if (!currentStatus.equals("Inactive")) {
			mainWindow.notificationsTxt.setText("Offer already launched.");
			return;
		}
		
		// Verify if a transfer is in progress
		for (int k = 0; k < mainWindow.dtm.getRowCount(); k++) {
			if (Integer.parseInt((String)mainWindow.dtm.getValueAt(k, 4)) > 0 &&
					Integer.parseInt((String)mainWindow.dtm.getValueAt(k, 4)) < 100) {
				
				mainWindow.notificationsTxt.setText("Cannot Launch Offer while a tranfer is in progress. Please wait...");
				return;
			}
		}
		
		String service = (String)mainWindow.dtm.getValueAt(selectedRow, 0);
		
		// Update DB:
		ws.launchOffer(cr.username, service);
		
		// Store the selected service for the refresh mechanism 
		offersLaunched.put(service, selectedRow);

		ArrayList<String> al = getAvailableSellers(service);
		
		int i = 1;
		for (String s : al) {
			
			String[] split = s.split(";");
			String seller_name = split[0];
			String price = split[1];
			
			mainWindow.dtm.insertRow(selectedRow + i, new String[]{"",seller_name,price,"No Offer","0"});
			i++;
			
			ws.addAuction(cr.username, service, seller_name);
		}
		mainWindow.dtm.setValueAt(al.size(), selectedRow, 1);
		mainWindow.dtm.setValueAt("", selectedRow, 3);
		
		
	}

	public void registerPopupItemDropOffer(PopupItemDropOffer popupItemDropOf) {
		popupItemDropOffer = popupItemDropOf;
	}

	public void clickPopupItemDropOffer() {
		logger.info("Offer has been dropped on demand.");
		
		if (mainWindow.dtm.getValueAt(selectedRow, 3).equals("Inactive")) {
			mainWindow.notificationsTxt.setText("Cannot Drop an Inactive Offer.");
			return;
		}
		
		// Verify if a transfer is in progress
		for (int k = 0; k < mainWindow.dtm.getRowCount(); k++) {
			if (Integer.parseInt((String)mainWindow.dtm.getValueAt(k, 4)) > 0 &&
					Integer.parseInt((String)mainWindow.dtm.getValueAt(k, 4)) < 100) {
				
				mainWindow.notificationsTxt.setText("Cannot Drop Offer while a tranfer is in progress. Please wait...");
				return;
			}
		}
		
				
		int nr = (int)mainWindow.dtm.getValueAt(selectedRow, 1);
		String service = (String)mainWindow.dtm.getValueAt(selectedRow, 0);
		
		// update HashMap
		offersLaunched.remove(service);
		
		for (int i = 0 ; i < nr; i++) {
			mainWindow.dtm.removeRow(selectedRow+1);
		}
		mainWindow.dtm.setValueAt("", selectedRow, 1);
		mainWindow.dtm.setValueAt("Inactive", selectedRow, 3);
		
		// Update DB:
		ws.dropOffer(cr.username, service);
	}

	public void registerPopupItemAcceptOffer(PopupItemAcceptOffer popupItemAcceptOf) {
		popupItemAcceptOffer = popupItemAcceptOf;
	}

	public void clickPopupItemAcceptOffer() {
		String currentStatus = (String)mainWindow.dtm.getValueAt(selectedRow, 3);
		if (!currentStatus.equals("Offer Made")) {
			mainWindow.notificationsTxt.setText("Offer cannot be accepted.");
			return;
		}
		
		mainWindow.dtm.setValueAt("Offer Accepted", selectedRow, 3);
		
		String service = "";
		// Get selected service
		int k = 1;
		while (service == "") {
			service = (String)mainWindow.dtm.getValueAt(selectedRow-k, 0);
			k++;
		}
		k--;
		
		String seller = (String)mainWindow.dtm.getValueAt(selectedRow, 1);
		ws.acceptOffer(cr.username, service, seller);
		
		int nr = (Integer)mainWindow.dtm.getValueAt(selectedRow - k, 1);
		int selectedSeller = selectedRow;
		selectedRow = selectedRow - k;
		
		for (int i = 1 ; i <= nr; i++) {
			selectedRow++;
			if (selectedSeller != selectedRow) {
				clickPopupItemRefuseOffer();	
			}	
		}
		
		// set selectedRow to initial value
		selectedRow = selectedSeller;
		mainWindow.dtm.setValueAt("Transfer Started", selectedRow, 3);
		
		// Start network transfer, after 100 ms
		int DELAY = 500;
		try {
			Thread.sleep(DELAY);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		transfer(selectedSeller);
	
	}

	public void registerPopupItemRefuseOffer(PopupItemRefuseOffer popupItemRefuseOf) {
		popupItemRefuseOffer =  popupItemRefuseOf;
	}

	public void clickPopupItemRefuseOffer() {
		String currentStatus = (String)mainWindow.dtm.getValueAt(selectedRow, 3);
		if (currentStatus.equals("Offer Refused")) {
			mainWindow.notificationsTxt.setText("Offer already refused.");
			return;
		}
		
		mainWindow.dtm.setValueAt("Offer Refused", selectedRow, 3);
		mainWindow.dtm.setValueAt("-1", selectedRow, 4);
		
		String seller = (String)mainWindow.dtm.getValueAt(selectedRow, 1);
		String service = "";
		
		// Get selected service
		int i = 1;
		while (service == "") {
			service = (String)mainWindow.dtm.getValueAt(selectedRow-i, 0);
			i++;
		}
			
		ws.refuseOffer(cr.username, service, seller);
	}

	public void registerPopupItemMakeOffer(PopupItemMakeOffer popupItemMakeOf) {
		popupItemMakeOffer = popupItemMakeOf;
	}

	public void clickPopupItemMakeOffer() {
		String currentStatus = (String)mainWindow.dtm.getValueAt(selectedRow, 3);
		if (currentStatus.equals("Offer Made")) {
			mainWindow.notificationsTxt.setText("Offer already made.");
			return;
		}
		
		mainWindow.dtm.setValueAt("Offer Made", selectedRow, 3);
		
		String buyer = (String)mainWindow.dtm.getValueAt(selectedRow, 1);
		String service = "";
		
		// Get selected service
		int i = 1;
		while (service == "") {
			service = (String)mainWindow.dtm.getValueAt(selectedRow-i, 0);
			i++;
		}
			
		ws.makeOffer(cr.username, service, buyer);

	}

	public void registerPopupItemDropAuction(PopupItemDropAuction popupItemDropAuc) {
		popupItemDropAuction = popupItemDropAuc;
	}

	public void clickPopupItemDropAuction() {
		String currentStatus = (String)mainWindow.dtm.getValueAt(selectedRow, 3);
		if (currentStatus.equals("No Offer"))
			return;
		
		mainWindow.dtm.setValueAt("No Offer", selectedRow, 3);
		
		String buyer = (String)mainWindow.dtm.getValueAt(selectedRow, 1);
		String service = "";
		
		// Get selected service
		int i = 1;
		while (service == "") {
			service = (String)mainWindow.dtm.getValueAt(selectedRow-i, 0);
			i++;
		}
			
		ws.dropAuction(cr.username, service, buyer);
	}

	public void setProgress(Integer newValue, int row) {
		logger.info("Proccesing transfer: " + newValue + "%");
		
		mainWindow.dtm.setValueAt(""+newValue, row, 4);
		
		if (newValue > 10) {
			mainWindow.dtm.setValueAt("Transfer in Progress", row, 3);
		}
		if (newValue == 100) { 
			mainWindow.dtm.setValueAt("Transfer Completed", row, 3);
		}
	}
	
	// Method used to change the status of an offer 
	public void setStatus(String service, String offerer, String newStatus) {
		logger.info("Offer status changed");
		
		String serviceName = new String();
		String offerName = new String();
		boolean offerFound = false; // flag
		int row = 0;
		// Find the corresponding row
		for (int k = 0; k < mainWindow.dtm.getRowCount(); k++) {
			serviceName = (String)mainWindow.dtm.getValueAt(k, 0);
			if (serviceName.equals(service)) {
				if (mainWindow.dtm.getValueAt(k, 3).equals("Inactive"))
					break;
				// Get services number
				int nr = (int)mainWindow.dtm.getValueAt(k, 1);
				
				for (int j = k+1; j <= k+nr; j++) {
					offerName = (String)mainWindow.dtm.getValueAt(j, 1);
					if (offerName.equals(offerer)) {
						offerFound = true;
						row = j;
						break;
					}
				}
			}
			if (offerFound)
				break;
		}

		// Offer was not found 
		if (!offerFound)
			return;
		
		switch (newStatus) {
		case "Offer Made":
			mainWindow.dtm.setValueAt("Offer Made", row, 3);
			break;
		case "Offer Exceeded":
			mainWindow.dtm.setValueAt("Offer Exceeded", row, 3);
			break;
		case "Offer Acdepted":
			mainWindow.dtm.setValueAt("Offer Accepted", row, 3);
			break;
		case "Transfer Failed":
			mainWindow.dtm.setValueAt("Transfer Failed", row, 3);
			mainWindow.dtm.setValueAt("-2", row, 4);
			break;
			
		default:
			break;
		}
	}

	@Override
	public void transferFailed(int row) {
		logger.error("Transfer failed");
		
		mainWindow.dtm.setValueAt("Transfer Failed", row, 3);
		mainWindow.dtm.setValueAt("-2", row, 4);
		
	}
	
	public void refresh(){		
		
		if (cr.type.equals("Buyer")){

			// Check if there is something to refresh
			if (offersLaunched.isEmpty()) {
				mainWindow.notificationsTxt.setText("You haven't made any changes. There is nothing to refresh.");
				return; 
			}
			
			// Clear view
			int nrRows = mainWindow.dtm.getRowCount();
			for (int i = nrRows-1; i >= 0; i--) 
				mainWindow.dtm.removeRow(i);

			for (String s : cr.services) {
				
				String[] split = s.split(";");
				String service_name = split[0];
				
				mainWindow.dtm.addRow(new String[] {service_name,"","","Inactive", "-1"});
			}

			
			for (String service : offersLaunched.keySet()) {
				
				// Get sellers for the service
				ArrayList<String> al = refreshSellers(service);
				
				int i = 1, j = 0;
				boolean flag = false;
				
				for (String s : al) {
					
					String[] split = s.split(";");
					String seller_name = split[0];
					String price = split[1];
					String status = split[2];
					
					if (flag == false) {
						for (j = 0; j < mainWindow.dtm.getRowCount(); j++)
							if (mainWindow.dtm.getValueAt(j, 0).equals(service)) { 
								flag = true;
								break;
							}
					}
					
					mainWindow.dtm.insertRow(j + i, new String[]{"",seller_name,price,status,"0"});
					i++;
				}
				
				mainWindow.dtm.setValueAt(al.size(), j, 1);
				mainWindow.dtm.setValueAt("", j, 3);
			}				
		} 
		else
		{
			// Clear view
			int nrRows = mainWindow.dtm.getRowCount();
			for (int i = nrRows-1; i >= 0; i--) 
				mainWindow.dtm.removeRow(i);

			for (String s : cr.services) {
				String[] split = s.split(";");
				String service_name = split[0];
				
				mainWindow.dtm.addRow(new String[] {service_name,"","","", "-1"});
			}
			
			for (int i = cr.services.size() - 1 ; i >= 0 ; i--){
				String service = cr.services.get(i);
				String[] split = service.split(";");
				String service_name = split[0];
				
				ArrayList<String> al = getAvailableBuyers(service_name , cr.username);
				
				int j = 1;
				for (String s : al) {
					String[] split2 = s.split(";");
					String buyer_name = split2[0];
					String price = split2[1];
					String status = split2[2];
					mainWindow.dtm.insertRow(i + 1, new String[]{"",buyer_name,price,status,"0"});
					j++;
				}
				mainWindow.dtm.setValueAt(al.size(), i, 1);
			}
		}
	}

}
